package service

import (
	"com.cracker/src/com.cracker/app/exception"
	"com.cracker/src/com.cracker/protobuf"
	"encoding/binary"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes/any"
	log "github.com/sirupsen/logrus"
	"io"
	"net"
	"strconv"
	"time"
)

type Client struct {
	conn      net.Conn
	Address   string
	Heartbeat int
	Password  string
	Port      int
	HttpPort  int
	Token     string
	Uid       string
}

func (client *Client) Connect() {
	conn, err := net.Dial("tcp", client.Address+":"+strconv.Itoa(client.Port))
	if err != nil {
		log.Error("建立连接失败:", err.Error())
		go client.ReConnect()
		return
	}
	log.Warn("建立连接成功")
	client.conn = conn
	client.login()

	go client.heartBeating()
	//
	for {
		lengthBuf := make([]byte, 4)
		_, err := io.ReadFull(conn, lengthBuf)
		bodyLen := binary.BigEndian.Uint32(lengthBuf)
		if bodyLen < 1 {
			continue
		}
		log.Warn("接收字节数:", bodyLen)
		bodyBuf := make([]byte, bodyLen)
		_, err = io.ReadFull(conn, bodyBuf)
		if err != nil {
			log.Error("读取数据错误:", err.Error())
			go client.ReConnect()
			return
		}
		msg := &protobuf.Data{}
		proto.Unmarshal(bodyBuf, msg)
		go client.handler(msg)
	}

}

func (client *Client) login() {
	// 登录
	user := &protobuf.User{}
	user.Uid = client.Uid
	user.Token = client.Token
	userData, _ := proto.Marshal(user)

	data := &any.Any{}
	data.Value = userData
	data.TypeUrl = "./com.cracker.dingding.console.protobuf.User"
	msg := &protobuf.Data{}
	msg.Type = 2
	msg.CmdType = 1
	msg.Data = data
	client.send(msg)
}

func (client *Client) handler(data *protobuf.Data) {
	appService := &AppService{}
	log.Warn("接收指令:", data.GetCmdType())
	if data.GetCmdType() == 1 {
		// 输出登录信息
		user := protobuf.User{}
		proto.Unmarshal(data.Data.GetValue(), &user)
		log.Warn("uid：", user.Uid)
	}
	if data.GetCmdType() == 2 {
		appService.WakeUp(client.Password)
		appService.StartApp()
	}
	if data.GetCmdType() == 3 {
		appService.WakeUp(client.Password)
		appService.StartApp()
		time.Sleep(5 * time.Second)
		appService.getScreen(client.Uid, client.Address+":"+strconv.Itoa(client.HttpPort))
		appService.AppSleep()
	}
}
func (client *Client) send(data *protobuf.Data) error {
	if client.conn == nil {
		log.Error("未连接到master节点,尝试重连")
		go client.ReConnect()
		return &exception.AppException{Msg: "未连接到master节点"}
	}
	bodyBytes, _ := proto.Marshal(data)
	//timeout := client.conn.SetWriteDeadline(time.Now().Add(3 * time.Second))
	//if timeout != nil {
	//	log.Error("发送超时")
	//	return &exception.AppException{Msg: "写入字节超时"}
	//}
	//生成长度
	bodyLen := uint32(len(bodyBytes))
	var dataBytes = make([]byte, 4)
	binary.BigEndian.PutUint32(dataBytes[:4], bodyLen)

	for _, tcpByte := range bodyBytes {
		dataBytes = append(dataBytes, tcpByte)
	}
	_, err := client.conn.Write(dataBytes)
	if err != nil {
		log.Error("发送失败")
		return err
	}
	return nil
}
func (client *Client) heartBeating() {
	select {
	case <-time.After(time.Duration(client.Heartbeat) * time.Second):
		data := &protobuf.Data{}
		data.Type = 1
		err := client.send(data)
		if err == nil {
			go client.heartBeating()
		} else {
			client.ReConnect()
		}
	}
}

func (client *Client) ReConnect() {
	log.Warn("断线重连")
	if client.conn != nil {
		client.conn.Close()
	}
	time.Sleep(1 * time.Second)
	client.Connect()
}
